package org.axed.user.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import java.util.ArrayList;
import java.util.Iterator;
import org.axed.user.client.util.IdxTower;

/**
 * TODO
 */
public class HtmlString extends IndexString {
	/**
	 * A "list" which determines the type of each entry.
	 *
	 * 'c' ... a normal character.
	 * 'x' ... a html entry
	 * ' ' ... a space
	 */
	private StringBuffer types;

	private ArrayList listeners = new ArrayList();

	/**
	 * Stores the positions of column entries
	 */
	IdxTower idx = null;
	
	/**
	 * Gets incremented whenever something changes the string that changes its character
	 * positions on screen. I.e. for example adding html span tags does not change 
	 * the version.
	 */
	public int version;

	/**
	 * Constructor;
	 */
	public HtmlString() {
		super();
		types = new StringBuffer();
		idx = new IdxTower();
		version = 0;
	}

	/**
	 * Constructor.
	 */
	public HtmlString(String s) {
		super(s);
		types = null;
		idx = null;
		version = 0;
	}

	/**
	 * Adds a listener that will be called whenever this string changes.
	 */
	public void addListener(HtmlStringListener l) {
		listeners.add(l);
	}
	
	/**
	 * Appends a column
	 */
	public void appendCol(String s) {
		if (types == null) {
			buildTower();
		}
		// possible insert the col before terminating non-visible html entries.
		/*int ix = types.length() - 1;
		boolean insert = false;
		while (ix >= 0 && types.charAt(ix) == 'x') {
			ix--;
			insert = true;
		}
		if (ix < 0) {
			// there were only non-visible html entries, normal append.
			insert = false;
		}
		if (insert) {
			insertEntry(s, ix);
		} else {
			appendEntry(s);
		}*/
		appendEntry(s);
	}

	/**
	 * Appends an entry
	 */
	public void appendEntry(String s) {
		super.appendEntry(s);

		char t = getTypeOf(s);
		types.append(t);
		if (t != 'x') {
			version++;
			idx.add(1);
		} else {
			idx.add(0);
		}

		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onEntryAppend(this, s);
		}
	}

	/**
	 * Clears the HtmlString.
	 */
	public void clear() {
		super.clear();
		if (types != null) {
			types.delete(0, types.length());
			idx.clear();
		}
		version++;

		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onStringClear(this);
		}
	}

	/**
	 * Returns a debug string.
	 */
	public String debug() {
		String s = super.debug();	
		s += "\" ";	
		s += "T:" + types + " ";
		s += "II:" + idx.debug() + ":II";
		return s;
	}

	/**
	 * Gets the nearest column of an entry (left of it).
	 */
	public int colOf(int entry) {
		if (idx == null) {
			buildTower();
		}
		int c = idx.get(entry);
		return c;
	}

	/**
	 * Gets the entry number of a column in text.
	 */
	public int entryOf(int col) {
		if (idx == null) {
			buildTower();
		}
		int e = idx.reverseGet(col);
		if (e < 0) {
			return -1; 
		}
		while (e < (entries() - 1) && getType(e) == 'x') {
			e++;
		}
		return e;
	}
	
	/**
	 * Returns the typeString
	 */
	public String getTypes() {
		if (types == null) {
			buildTower();
		}
		return types.toString();
	}

	/**
	 * Returns the type of an entry with string s
	 */
	public static char getTypeOf(String s) {
		if (s.length() == 0) { // a non existant entry
			return 'x';
		}
		switch (s.charAt(0)) {
		case '<' : return 'x';
		case ' ' : return ' ';
		default  : return 'c';
		}
	}

	/**
	 * Returns the type of entry p
	 */
	public char getType(int p) {
		return types.charAt(p);
	}

	/**
	 * Returns true when this string has at least
	 * one entry of type 'c'.
	 */
	public boolean hasCharacters() {
		int e = entries();
		if (e <= 0) {
			return false;
		}
		for (int i = 0; i < e; i++) {
			if (getType(i) == 'c') {
				return true;
			}
		}
		return false;
	}

	/**
	 * Inserts a column at col.
	 */
	public void insertCol(String s, int col) {
		insertEntry(s, entryOf(col));
	}

	/**
	 * Inserts an entry at p.
	 */
	public void insertEntry(String s, int p) {
		super.insertEntry(s, p);
		
		char t = getTypeOf(s);
		types.insert(p, t);
		if (t != 'x') {
			version++;
			idx.insert(1, p);
		} else {
			idx.insert(0, p);
		}

		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onEntryInsert(this, p, s);
		}
	}

	/**
	 * Removes a column
	 */
	public void removeCol(int c) {
		int e = entryOf(c);
		removeEntry(e);
	}

	/**
	 * Deletes column entries vom start until end. 
	 * But it leaves in the anchors!
	 */
	public void removeCols(int start, int end) {
		for (int i = start; i < end; i++) {
			removeCol(start);
		}
	}

	/**
	 * Removes an entry.
	 */
	public void removeEntry(int p) {
		super.removeEntry(p);
		if (types.charAt(p) != 'x') {
			version++;
		}
		idx.remove(p);
		types.deleteCharAt(p);

  		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onEntryRemove(this, p);
		}
	}

	/**
	 * Replaces a col.
	 */
	public void replaceCol(String s, int col) {
		replaceEntry(s, entryOf(col));
	}

	/**
	 * Replaces an entry.
	 */
	public void replaceEntry(String s, int p) {
		super.replaceEntry(s, p);

		char t = getTypeOf(s);
		if (!(t == 'x' && types.charAt(p) == 'x')) {
			version++;
		}
		// TODO: unalter version when both chars and monospaced
		types.setCharAt(p, getTypeOf(s));

		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onEntryReplace(this, p, s);
		}
	}

	/**
 	 * Retrieves the col at c
 	 */
	public String retrieveCol(int c) {
		int e = entryOf(c);
		return retrieveEntries(e, e + 1);
	}
	
	/**
	 * TODO
	 */
	public String retrieveCols(int bc, int ec) {
		return retrieveEntries(entryOf(bc), entryOf(ec));
	}

//protects

	protected void towerAdd(char firstC, int p) {
		super.towerAdd(firstC, p);
		switch (firstC) {
		case '<' : types.append('x');
				   idx.add(0);
		           return;
		case ' ' : types.append(' ');
				   idx.add(1);
		           return;
		default : types.append('c');
				   idx.add(1);
		           return;
		}	
	}

	protected void buildTower() {
		types = new StringBuffer();
		idx = new IdxTower();
		super.buildTower();

		for (Iterator it = listeners.iterator(); it.hasNext();) {
			((HtmlStringListener) it.next()).onRebuild(this);
		}
	}
}
